//
//  NSFileManager+FWPowerful.m
//  iOSToolkit
//
//  Created by Evan Xie on 3/13/15.
//  Copyright (c) 2015 Family Wings. All rights reserved.
//

#import "NSFileManager+FWPowerful.h"


#define OUTPUT_ERROR(fmt, ...)  NSLog((@"[%s]" fmt), __PRETTY_FUNCTION__, ##__VA_ARGS__);



@implementation NSFileManager (FWPowerful)

+ (NSArray *)fileURLsOfDirectoryAtURL:(NSURL *)directoryURL
              includingPropertiesKeys:(NSArray *)keys
                   enumerationOptions:(NSDirectoryEnumerationOptions)enumerationOptions
                          fileURLType:(FileURLType)fileURLType

{
    NSMutableArray *fileArray = [NSMutableArray arrayWithCapacity:0];
    NSString *errorMessage = nil;
    
    if (![self isValidNativeDirectoryFileURL:directoryURL withErrorMessage:&errorMessage]) {
        OUTPUT_ERROR(@"%@", errorMessage);
        return fileArray;
    }
    
    NSMutableArray *properties = [NSMutableArray arrayWithCapacity:0];
    [properties addObjectsFromArray:keys];
    
    if (![properties containsObject:NSURLIsDirectoryKey]) {
        [properties addObject:NSURLIsDirectoryKey];
    }
    
    NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:directoryURL includingPropertiesForKeys:properties options:enumerationOptions errorHandler:nil];
    
    for (NSURL *theURL in enumerator) {
        
        NSNumber *isDirectory = nil;
        NSNumber *isHidden = nil;
        [theURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];
        [theURL getResourceValue:&isHidden forKey:NSURLIsHiddenKey error:nil];
        
        if ([isDirectory boolValue] && fileURLType == FileURLTypeRegularFile) {
            // Skip the directories.
            continue;
        }
        
        if (fileURLType == FileURLTypeDirectory) {
            if (![isDirectory boolValue]) {
                continue;
            }
        }
        
        [fileArray addObject:theURL];
    }
    
    return fileArray;
}


+ (void)enumerateAtDirectoryURL:(NSURL *)directoryURL
        includingPropertiesKeys:(NSArray *)keys
             enumerationOptions:(NSDirectoryEnumerationOptions)enumerationOptions
                    fileURLType:(FileURLType)fileURLType
                    resultBlock:(void (^)(NSURL *, BOOL *))resultBlock
{
    if (resultBlock == NULL) {
        OUTPUT_ERROR(@"Parameter 'resultBlock' can not be null");
        return;
    }
    
    BOOL stop = NO;
    NSString *errorMessage = nil;
    if (![self isValidNativeDirectoryFileURL:directoryURL withErrorMessage:&errorMessage]) {
        stop = YES;
        OUTPUT_ERROR(@"%@", errorMessage);
        resultBlock(nil, &stop);
        return ;
    }
    
    NSMutableArray *properties = [NSMutableArray arrayWithCapacity:0];
    [properties addObjectsFromArray:keys];
    
    if (![properties containsObject:NSURLIsDirectoryKey]) {
        [properties addObject:NSURLIsDirectoryKey];
    }

    NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:directoryURL includingPropertiesForKeys:properties options:enumerationOptions errorHandler:nil];
    NSURL *theFileURL = nil;
    while ((theFileURL = enumerator.nextObject) && (stop == NO)) {
        NSNumber *isDirectory = nil;
        [theFileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];
        
        if ([isDirectory boolValue] && fileURLType == FileURLTypeRegularFile) {
            // Skip the directories.
            continue;
        }
        
        if (fileURLType == FileURLTypeDirectory) {
            if (![isDirectory boolValue]) {
                continue;
            }
        }
        
        [self invokeFileURLBlock:resultBlock withFileURL:theFileURL stop:stop];
    }
    
    // Finished, maybe all the files are enumerated, or an error occurs.
    stop = YES;
    [self invokeFileURLBlock:resultBlock withFileURL:nil stop:stop];
}


+ (NSURL *)nextAvailableURLForFileAtURL:(NSURL *)fileURL
{
    if (!fileURL.isFileURL) {
        return nil;
    }
    
    NSURL *destinationDirectoryURL = [fileURL URLByDeletingLastPathComponent];
    NSString *attemptedFilename = fileURL.lastPathComponent;
    NSString *fileBasename = [attemptedFilename stringByDeletingPathExtension];
    NSString *extension = [attemptedFilename pathExtension];
    NSUInteger filenameSuffix = 0;
    
    NSURL *availableURL = [fileURL copy];
    
    while ([[NSFileManager defaultManager] fileExistsAtPath:fileURL.path]) {
        
        filenameSuffix++;
        attemptedFilename = [NSString stringWithFormat:@"%@ (%lu)", fileBasename, (unsigned long)filenameSuffix];
        
        if (extension) {
            attemptedFilename = [attemptedFilename stringByAppendingPathExtension:extension];
        }
        
        availableURL = [destinationDirectoryURL URLByAppendingPathComponent:attemptedFilename];
    }
    
    return availableURL;
}


#pragma mark - Internal Assistants

+ (BOOL)isValidNativeDirectoryFileURL:(NSURL *)directoryURL withErrorMessage:(NSString **)errorMessage
{
    if (directoryURL == nil) {
        if (errorMessage) {
            *errorMessage = @"directoryURL can not be nil";
        }
        return NO;
    }
    
    if (!directoryURL.isFileURL) {
        if (errorMessage) {
            *errorMessage = @"directoryURL is not file url(file://)";
        }
        return NO;
    }
    
    BOOL isDirectory = NO;
    if ([[NSFileManager defaultManager] fileExistsAtPath:directoryURL.path isDirectory:&isDirectory]) {
        if (!isDirectory) {
            if (errorMessage) {
                *errorMessage = @"directoryURL is not a normal file";
            }
            return NO;
        }
    } else {
        if (errorMessage) {
            *errorMessage = @"directoryURL doesn't exist";
        }
        return NO;
    }
    
    return YES;
}


+ (void)invokeFileURLBlock:(void (^)(NSURL *, BOOL *))resultBlock withFileURL:(NSURL *)fileURL stop:(BOOL)stop
{
    if (resultBlock) {
        resultBlock (fileURL, &stop);
    }
}

@end
